package com.train.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

public class ApplicationContext {
    private Class configClass;

    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String,Object> singleObjects = new ConcurrentHashMap<>();
    private ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
    public ApplicationContext(Class configClass) {
        this.configClass = configClass;
        /**
         * 扫描
         * */
        // 判断configClass是否标有CompoentScan注解
        if (configClass.isAnnotationPresent(CompoentScan.class)) {
            // 获取CompoentScan中传入的value值
            CompoentScan scan = (CompoentScan) configClass.getAnnotation(CompoentScan.class);
            String scanPath = scan.value().replace(".","/");// 获取扫描路径
            ClassLoader classLoader = ApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(scanPath);
            File file =new File( resource.getFile() );
            // 判断包是否是一个文件夹
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                for (File f : files) {
                    String fileName = f.getAbsolutePath();
                    // 拿到绝对路径，以绝对路径判断是否是一个java文件
                    if ( fileName.endsWith(".class")) {
                        try {
                            String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class")).replace("\\",".");
                            // 通过类加载器拿到class，用反射判断此类是否有Compoent注解
                            Class<?> clazz = classLoader.loadClass(className);
                            if (clazz.isAnnotationPresent(Component.class)) {
                                if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                    BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();
                                    beanPostProcessorList.add(instance);
                                }
                                /**
                                 * 是Bean
                                 * */
                                // 获取bean名字
                                Component compoent = clazz.getAnnotation(Component.class);
                                //beanName是空值，则使用小驼峰命名法作为beanName
                                String beanName = compoent.value().isEmpty() ? Introspector.decapitalize(clazz.getSimpleName()):compoent.value();

                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setClassType(clazz);
                                // 判断类是否带有scope注解，没有scopse注解，默认是单例bean
                                if (clazz.isAnnotationPresent(Scope.class)) {
                                    Scope scope = clazz.getAnnotation(Scope.class);
                                    beanDefinition.setScope(scope.value());
                                } else {
                                    beanDefinition.setScope(ScopeType.single);
                                }
                                // 将此bean存入map中
                                beanDefinitionMap.put(beanName,beanDefinition);
                            }
                        } catch (ClassNotFoundException e) {
                            throw new RuntimeException(e);
                        } catch (InstantiationException e) {
                            throw new RuntimeException(e);
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        }
        // 将所有单例bean生成出来
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope() == ScopeType.single) {
                Object bean = createBean(beanName,beanDefinition);//生成实例Bean
                singleObjects.put(beanName,bean);//将此Bean存入容器
            }
        }
    }
    private Object createBean(String beanName,BeanDefinition beanDefinition){
        try {
            Class classType = beanDefinition.getClassType();
            Object bean = classType.getConstructor().newInstance();
            // 依赖注入
            for (Field field : classType.getDeclaredFields()) {
                 if (field.isAnnotationPresent(Autowired.class)){
                     field.setAccessible(true);
                     field.set(bean,getBean(field.getName()));
                 }
            }
            //aware
            if(bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            // 初始化前
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                beanPostProcessor.postProcessorBeforeInitialization(beanName,bean);
            }
            //初始化
            if (bean instanceof InitializingBean){
                ((InitializingBean) bean).afterPropertiesSet();
            }
            // 初始化后
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                beanPostProcessor.postProcessorAfterInitialization(beanName,bean);
            }
            return bean;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }
    public Object getBean(String beanName){
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null){
            throw new NullPointerException();
        } else {
            ScopeType scopeType = beanDefinition.getScope();
            if (scopeType == ScopeType.single) {
                // 单例bean
                Object bean = singleObjects.get(beanName);
                if (bean == null){
                   Object newBean =  createBean(beanName,beanDefinition);
                   singleObjects.put(beanName,newBean);
                }
                return bean;
            }else {
                // 多例bean
                return createBean(beanName,beanDefinition);
            }
        }
    }
}
